/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.naming.cosnaming;

import org.omg.CosNaming.NamingContextExtPackage.*;
import java.io.StringWriter;

// Import general CORBA classes
import org.omg.CORBA.SystemException;
import org.omg.CORBA.Object;

// Import org.omg.CosNaming types
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;

/**
 * Class InteroperableNamingImpl implements the methods defined
 * for NamingContextExt which is part of Interoperable Naming
 * Service specifications. This class is added for doing more
 * of Parsing and Building of Stringified names according to INS
 * Spec.
 */
public class InterOperableNamingImpl {
    /**
      * Method which stringifies the Name Components given as the input
      * parameter.
      *
      * @param n Array of Name Components (Simple or Compound Names)
      * @return string which is the stringified reference.
      */
    public String convertToString(org.omg.CosNaming.NameComponent[] theNameComponents) {
        String theConvertedString = convertNameComponentToString(theNameComponents[0]);
        String temp;
        for (int i = 1; i < theNameComponents.length; i++) {
            temp = convertNameComponentToString(theNameComponents[i]);
            if (temp != null) {
                theConvertedString = theConvertedString + "/" + convertNameComponentToString(theNameComponents[i]);
            }
        }
        return theConvertedString;
    }

    /** This method converts a single Namecomponent to String, By adding Escapes
     *  If neccessary.
     */
    private String convertNameComponentToString(org.omg.CosNaming.NameComponent theNameComponent) {
        if (((theNameComponent.id == null) || (theNameComponent.id.length() == 0)) && ((theNameComponent.kind == null) || (theNameComponent.kind.length() == 0))) {
            return ".";
        } else if ((theNameComponent.id == null) || (theNameComponent.id.length() == 0)) {
            String kind = addEscape(theNameComponent.kind);
            return "." + kind;
        } else if ((theNameComponent.kind == null) || (theNameComponent.kind.length() == 0)) {
            String id = addEscape(theNameComponent.id);
            return id;
        } else {
            String id = addEscape(theNameComponent.id);
            String kind = addEscape(theNameComponent.kind);
            return (id + "." + kind);
        }
    }

    /** This method adds escape '\' for the Namecomponent if neccessary
     */
    private String addEscape(String value) {
        StringBuffer theNewValue;
        if ((value != null) && ((value.indexOf('.') != -1) || (value.indexOf('/') != -1))) {
            char c;
            theNewValue = new StringBuffer();
            for (int i = 0; i < value.length(); i++) {
                c = value.charAt(i);
                if ((c != '.') && (c != '/')) {
                    theNewValue.append(c);
                } else {
                    // Adding escape for the "."
                    theNewValue.append('\\');
                    theNewValue.append(c);
                }
            }
        } else {
            return value;
        }
        return new String(theNewValue);
    }

    /**
      * Method which converts the Stringified name into Array of Name Components.
      *
      * @param string which is the stringified name.
      * @return  Array of Name Components (Simple or Compound Names)
      */
    public org.omg.CosNaming.NameComponent[] convertToNameComponent(String theStringifiedName) throws org.omg.CosNaming.NamingContextPackage.InvalidName {
        String[] theStringifiedNameComponents = breakStringToNameComponents(theStringifiedName);
        if ((theStringifiedNameComponents == null) || (theStringifiedNameComponents.length == 0)) {
            return null;
        }
        NameComponent[] theNameComponents = new NameComponent[theStringifiedNameComponents.length];
        for (int i = 0; i < theStringifiedNameComponents.length; i++) {
            theNameComponents[i] = createNameComponentFromString(theStringifiedNameComponents[i]);
        }
        return theNameComponents;
    }

    /** Step1 in converting Stringified name into  array of Name Component
      * is breaking the String into multiple name components
      */
    private String[] breakStringToNameComponents(String theStringifiedName) {
        int[] theIndices = new int[100];
        int theIndicesIndex = 0;

        for (int index = 0; index <= theStringifiedName.length();) {
            theIndices[theIndicesIndex] = theStringifiedName.indexOf('/', index);
            if (theIndices[theIndicesIndex] == -1) {
                // This is the end of all the occurence of '/' and hence come
                // out of the loop
                index = theStringifiedName.length() + 1;
            } else {
                // If the '/' is found, first check whether it is
                // preceded by escape '\'
                // If not then set theIndices and increment theIndicesIndex
                // and also set the index else just ignore the '/'
                if ((theIndices[theIndicesIndex] > 0) && (theStringifiedName.charAt(theIndices[theIndicesIndex] - 1) == '\\')) {
                    index = theIndices[theIndicesIndex] + 1;
                    theIndices[theIndicesIndex] = -1;
                } else {
                    index = theIndices[theIndicesIndex] + 1;
                    theIndicesIndex++;
                }
            }
        }
        if (theIndicesIndex == 0) {
            String[] tempString = new String[1];
            tempString[0] = theStringifiedName;
            return tempString;
        }
        if (theIndicesIndex != 0) {
            theIndicesIndex++;
        }
        return StringComponentsFromIndices(theIndices, theIndicesIndex, theStringifiedName);
    }

    /** This method breaks one big String into multiple substrings based
      * on the array of index passed in.
      */
    private String[] StringComponentsFromIndices(int[] theIndices, int indicesCount, String theStringifiedName) {
        String[] theStringComponents = new String[indicesCount];
        int firstIndex = 0;
        int lastIndex = theIndices[0];
        for (int i = 0; i < indicesCount; i++) {
            theStringComponents[i] = theStringifiedName.substring(firstIndex, lastIndex);
            if ((theIndices[i] < theStringifiedName.length() - 1) && (theIndices[i] != -1)) {
                firstIndex = theIndices[i] + 1;
            } else {
                firstIndex = 0;
                i = indicesCount;
            }
            if ((i + 1 < theIndices.length) && (theIndices[i + 1] < (theStringifiedName.length() - 1)) && (theIndices[i + 1] != -1)) {
                lastIndex = theIndices[i + 1];
            } else {
                i = indicesCount;
            }
            // This is done for the last component
            if (firstIndex != 0 && i == indicesCount) {
                theStringComponents[indicesCount - 1] = theStringifiedName.substring(firstIndex);
            }
        }
        return theStringComponents;
    }

    /** Step 2: After Breaking the Stringified name into set of NameComponent
      * Strings, The next step is to create Namecomponents from the substring
      * by removing the escapes if there are any.
      */
    private NameComponent createNameComponentFromString(String theStringifiedNameComponent) throws org.omg.CosNaming.NamingContextPackage.InvalidName

    {
        String id = null;
        String kind = null;
        if ((theStringifiedNameComponent == null) || (theStringifiedNameComponent.length() == 0) || (theStringifiedNameComponent.endsWith("."))) {
            // If any of the above is true, then we create an invalid Name
            // Component to indicate that it is an invalid name.
            throw new org.omg.CosNaming.NamingContextPackage.InvalidName();
        }

        int index = theStringifiedNameComponent.indexOf('.', 0);
        // The format could be XYZ (Without kind)
        if (index == -1) {
            id = theStringifiedNameComponent;
        }
        // The format is .XYZ (Without ID)
        else if (index == 0) {
            // This check is for the Namecomponent which is just "." meaning Id
            // and Kinds are null
            if (theStringifiedNameComponent.length() != 1) {
                kind = theStringifiedNameComponent.substring(1);
            }
        } else {
            if (theStringifiedNameComponent.charAt(index - 1) != '\\') {
                id = theStringifiedNameComponent.substring(0, index);
                kind = theStringifiedNameComponent.substring(index + 1);
            } else {
                boolean kindfound = false;
                while ((index < theStringifiedNameComponent.length()) && (kindfound != true)) {
                    index = theStringifiedNameComponent.indexOf('.', index + 1);
                    if (index > 0) {
                        if (theStringifiedNameComponent.charAt(index - 1) != '\\') {
                            kindfound = true;
                        }
                    } else {
                        // No more '.', which means there is no Kind
                        index = theStringifiedNameComponent.length();
                    }
                }
                if (kindfound == true) {
                    id = theStringifiedNameComponent.substring(0, index);
                    kind = theStringifiedNameComponent.substring(index + 1);
                } else {
                    id = theStringifiedNameComponent;
                }
            }
        }
        id = cleanEscapeCharacter(id);
        kind = cleanEscapeCharacter(kind);
        if (id == null) {
            id = "";
        }
        if (kind == null) {
            kind = "";
        }
        return new NameComponent(id, kind);
    }

    /** This method cleans the escapes in the Stringified name and returns the
      * correct String
      */
    private String cleanEscapeCharacter(String theString) {
        if ((theString == null) || (theString.length() == 0)) {
            return theString;
        }
        int index = theString.indexOf('\\');
        if (index == 0) {
            return theString;
        } else {
            StringBuffer src = new StringBuffer(theString);
            StringBuffer dest = new StringBuffer();
            char c;
            for (int i = 0; i < theString.length(); i++) {
                c = src.charAt(i);
                if (c != '\\') {
                    dest.append(c);
                } else {
                    if (i + 1 < theString.length()) {
                        char d = src.charAt(i + 1);
                        // If there is a AlphaNumeric character after a \
                        // then include slash, as it is not intended as an
                        // escape character.
                        if (Character.isLetterOrDigit(d)) {
                            dest.append(c);
                        }
                    }
                }
            }
            return new String(dest);
        }
    }

    /**
      * Method which converts the Stringified name  and Host Name Address into
      * a URL based Name
      *
      * @param address which is ip based host name
      * @param name which is the stringified name.
      * @return  url based Name.
      */
    public String createURLBasedAddress(String address, String name) throws InvalidAddress {
        String theurl = null;
        if ((address == null) || (address.length() == 0)) {
            throw new InvalidAddress();
        } else {
            theurl = "corbaname:" + address + "#" + encode(name);
        }
        return theurl;
    }

    /** Encodes the string according to RFC 2396 IETF spec required by INS.
     */
    private String encode(String stringToEncode) {
        StringWriter theStringAfterEscape = new StringWriter();
        int byteCount = 0;
        for (int i = 0; i < stringToEncode.length(); i++) {
            char c = stringToEncode.charAt(i);
            if (Character.isLetterOrDigit(c)) {
                theStringAfterEscape.write(c);
            }
            // Do no Escape for characters in this list
            // RFC 2396
            else if ((c == ';') || (c == '/') || (c == '?') || (c == ':') || (c == '@') || (c == '&') || (c == '=') || (c == '+') || (c == '$') || (c == ';') || (c == '-') || (c == '_') || (c == '.') || (c == '!') || (c == '~') || (c == '*') || (c == ' ') || (c == '(') || (c == ')')) {
                theStringAfterEscape.write(c);
            } else {
                // Add escape
                theStringAfterEscape.write('%');
                String hexString = Integer.toHexString((int) c);
                theStringAfterEscape.write(hexString);
            }
        }
        return theStringAfterEscape.toString();
    }

}
